// break it out on its own because
// it's building from the lodash-es from scratch
// according to this discussion https://github.com/lodash/lodash/issues/3298

/**
 * using just the map reduce to chain multiple functions together
 * @param {function} mainFn the init function
 * @param {array} moreFns as many as you want to take the last value and return a new one
 * @return {function} accept value for the mainFn
 */
var chainFns = function (mainFn) {
  var moreFns = [], len = arguments.length - 1;
  while ( len-- > 0 ) moreFns[ len ] = arguments[ len + 1 ];

  return (
  function () {
    var args = [], len = arguments.length;
    while ( len-- ) args[ len ] = arguments[ len ];

    return (
    moreFns.reduce(function (value, nextFn) { return (
      Reflect.apply(nextFn, null, [value])
    ); }, Reflect.apply(mainFn, null, args))
  );
  }
);
};

/**
 * previously we already make sure the order of the namespaces
 * and attach the auth client to it
 * @param {array} promises array of unresolved promises
 * @return {object} promise resolved with the array of promises resolved results
 */
function chainPromises(promises) {
  return promises.reduce(function (promiseChain, currentTask) { return (
    promiseChain.then(function (chainResults) { return (
      currentTask.then(function (currentResult) { return (
        chainResults.concat( [currentResult])
      ); })
    ); })
  ); }, Promise.resolve([]))
}

/**
 * this is essentially the same as the injectToFn
 * but this will not allow overwrite and set the setter and getter
 * @param {object} obj to get injected
 * @param {string} name of the property
 * @param {function} setter for set
 * @param {function} [getter=null] for get default return null fn
 * @return {object} the injected obj
 */
function objDefineProps(obj, name, setter, getter) {
  if ( getter === void 0 ) getter = null;

  if (Object.getOwnPropertyDescriptor(obj, name) === undefined) {
    Object.defineProperty(obj, name, {
      set: setter,
      get: getter === null ? function() { return null; } : getter
    });
  }
  return obj
}

/**
 * After the user login we will use this Object.define add a new property
 * to the resolver with the decoded user data
 * @param {function} resolver target resolver
 * @param {string} name the name of the object to get inject also for checking
 * @param {object} data to inject into the function static interface
 * @param {boolean} [overwrite=false] if we want to overwrite the existing data
 * @return {function} added property resolver
 */
function injectToFn(resolver, name, data, overwrite) {
  if ( overwrite === void 0 ) overwrite = false;

  var check = Object.getOwnPropertyDescriptor(resolver, name);
  if (overwrite === false && check !== undefined) {
    // console.info(`NOT INJECTED`)
    return resolver;
  }
  /* this will throw error!
  if (overwrite === true && check !== undefined) {
    delete resolver[name] // delete this property
  }
  */
  // console.info(`INJECTED`)
  Object.defineProperty(resolver, name, {
    value: data,
    writable: overwrite // if its set to true then we should able to overwrite it
  });

  return resolver;
}

/**
 * Checks if `value` is classified as an `Array` object.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an array, else `false`.
 * @example
 *
 * _.isArray([1, 2, 3]);
 * // => true
 *
 * _.isArray(document.body.children);
 * // => false
 *
 * _.isArray('abc');
 * // => false
 *
 * _.isArray(_.noop);
 * // => false
 */
var isArray = Array.isArray;

var global$1 = (typeof global !== "undefined" ? global :
            typeof self !== "undefined" ? self :
            typeof window !== "undefined" ? window : {});

/** Detect free variable `global` from Node.js. */
var freeGlobal = typeof global$1 == 'object' && global$1 && global$1.Object === Object && global$1;

/** Detect free variable `self`. */
var freeSelf = typeof self == 'object' && self && self.Object === Object && self;

/** Used as a reference to the global object. */
var root = freeGlobal || freeSelf || Function('return this')();

/** Built-in value references. */
var Symbol = root.Symbol;

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * Used to resolve the
 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
 * of values.
 */
var nativeObjectToString = objectProto.toString;

/** Built-in value references. */
var symToStringTag = Symbol ? Symbol.toStringTag : undefined;

/**
 * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the raw `toStringTag`.
 */
function getRawTag(value) {
  var isOwn = hasOwnProperty.call(value, symToStringTag),
      tag = value[symToStringTag];

  try {
    value[symToStringTag] = undefined;
    var unmasked = true;
  } catch (e) {}

  var result = nativeObjectToString.call(value);
  if (unmasked) {
    if (isOwn) {
      value[symToStringTag] = tag;
    } else {
      delete value[symToStringTag];
    }
  }
  return result;
}

/** Used for built-in method references. */
var objectProto$1 = Object.prototype;

/**
 * Used to resolve the
 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
 * of values.
 */
var nativeObjectToString$1 = objectProto$1.toString;

/**
 * Converts `value` to a string using `Object.prototype.toString`.
 *
 * @private
 * @param {*} value The value to convert.
 * @returns {string} Returns the converted string.
 */
function objectToString(value) {
  return nativeObjectToString$1.call(value);
}

/** `Object#toString` result references. */
var nullTag = '[object Null]',
    undefinedTag = '[object Undefined]';

/** Built-in value references. */
var symToStringTag$1 = Symbol ? Symbol.toStringTag : undefined;

/**
 * The base implementation of `getTag` without fallbacks for buggy environments.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the `toStringTag`.
 */
function baseGetTag(value) {
  if (value == null) {
    return value === undefined ? undefinedTag : nullTag;
  }
  return (symToStringTag$1 && symToStringTag$1 in Object(value))
    ? getRawTag(value)
    : objectToString(value);
}

/**
 * Creates a unary function that invokes `func` with its argument transformed.
 *
 * @private
 * @param {Function} func The function to wrap.
 * @param {Function} transform The argument transform.
 * @returns {Function} Returns the new function.
 */
function overArg(func, transform) {
  return function(arg) {
    return func(transform(arg));
  };
}

/** Built-in value references. */
var getPrototype = overArg(Object.getPrototypeOf, Object);

/**
 * Checks if `value` is object-like. A value is object-like if it's not `null`
 * and has a `typeof` result of "object".
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
 * @example
 *
 * _.isObjectLike({});
 * // => true
 *
 * _.isObjectLike([1, 2, 3]);
 * // => true
 *
 * _.isObjectLike(_.noop);
 * // => false
 *
 * _.isObjectLike(null);
 * // => false
 */
function isObjectLike(value) {
  return value != null && typeof value == 'object';
}

/** `Object#toString` result references. */
var objectTag = '[object Object]';

/** Used for built-in method references. */
var funcProto = Function.prototype,
    objectProto$2 = Object.prototype;

/** Used to resolve the decompiled source of functions. */
var funcToString = funcProto.toString;

/** Used to check objects for own properties. */
var hasOwnProperty$1 = objectProto$2.hasOwnProperty;

/** Used to infer the `Object` constructor. */
var objectCtorString = funcToString.call(Object);

/**
 * Checks if `value` is a plain object, that is, an object created by the
 * `Object` constructor or one with a `[[Prototype]]` of `null`.
 *
 * @static
 * @memberOf _
 * @since 0.8.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
 * @example
 *
 * function Foo() {
 *   this.a = 1;
 * }
 *
 * _.isPlainObject(new Foo);
 * // => false
 *
 * _.isPlainObject([1, 2, 3]);
 * // => false
 *
 * _.isPlainObject({ 'x': 0, 'y': 0 });
 * // => true
 *
 * _.isPlainObject(Object.create(null));
 * // => true
 */
function isPlainObject(value) {
  if (!isObjectLike(value) || baseGetTag(value) != objectTag) {
    return false;
  }
  var proto = getPrototype(value);
  if (proto === null) {
    return true;
  }
  var Ctor = hasOwnProperty$1.call(proto, 'constructor') && proto.constructor;
  return typeof Ctor == 'function' && Ctor instanceof Ctor &&
    funcToString.call(Ctor) == objectCtorString;
}

/**
 * A specialized version of `_.map` for arrays without support for iteratee
 * shorthands.
 *
 * @private
 * @param {Array} [array] The array to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Array} Returns the new mapped array.
 */
function arrayMap(array, iteratee) {
  var index = -1,
      length = array == null ? 0 : array.length,
      result = Array(length);

  while (++index < length) {
    result[index] = iteratee(array[index], index, array);
  }
  return result;
}

/** `Object#toString` result references. */
var symbolTag = '[object Symbol]';

/**
 * Checks if `value` is classified as a `Symbol` primitive or object.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
 * @example
 *
 * _.isSymbol(Symbol.iterator);
 * // => true
 *
 * _.isSymbol('abc');
 * // => false
 */
function isSymbol(value) {
  return typeof value == 'symbol' ||
    (isObjectLike(value) && baseGetTag(value) == symbolTag);
}

/** Used as references for various `Number` constants. */
var INFINITY = 1 / 0;

/** Used to convert symbols to primitives and strings. */
var symbolProto = Symbol ? Symbol.prototype : undefined,
    symbolToString = symbolProto ? symbolProto.toString : undefined;

/**
 * The base implementation of `_.toString` which doesn't convert nullish
 * values to empty strings.
 *
 * @private
 * @param {*} value The value to process.
 * @returns {string} Returns the string.
 */
function baseToString(value) {
  // Exit early for strings to avoid a performance hit in some environments.
  if (typeof value == 'string') {
    return value;
  }
  if (isArray(value)) {
    // Recursively convert values (susceptible to call stack limits).
    return arrayMap(value, baseToString) + '';
  }
  if (isSymbol(value)) {
    return symbolToString ? symbolToString.call(value) : '';
  }
  var result = (value + '');
  return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
}

/**
 * The base implementation of `_.slice` without an iteratee call guard.
 *
 * @private
 * @param {Array} array The array to slice.
 * @param {number} [start=0] The start position.
 * @param {number} [end=array.length] The end position.
 * @returns {Array} Returns the slice of `array`.
 */
function baseSlice(array, start, end) {
  var index = -1,
      length = array.length;

  if (start < 0) {
    start = -start > length ? 0 : (length + start);
  }
  end = end > length ? length : end;
  if (end < 0) {
    end += length;
  }
  length = start > end ? 0 : ((end - start) >>> 0);
  start >>>= 0;

  var result = Array(length);
  while (++index < length) {
    result[index] = array[index + start];
  }
  return result;
}

/**
 * Casts `array` to a slice if it's needed.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {number} start The start position.
 * @param {number} [end=array.length] The end position.
 * @returns {Array} Returns the cast slice.
 */
function castSlice(array, start, end) {
  var length = array.length;
  end = end === undefined ? length : end;
  return (!start && end >= length) ? array : baseSlice(array, start, end);
}

/**
 * The base implementation of `_.findIndex` and `_.findLastIndex` without
 * support for iteratee shorthands.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {Function} predicate The function invoked per iteration.
 * @param {number} fromIndex The index to search from.
 * @param {boolean} [fromRight] Specify iterating from right to left.
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function baseFindIndex(array, predicate, fromIndex, fromRight) {
  var length = array.length,
      index = fromIndex + (fromRight ? 1 : -1);

  while ((fromRight ? index-- : ++index < length)) {
    if (predicate(array[index], index, array)) {
      return index;
    }
  }
  return -1;
}

/**
 * The base implementation of `_.isNaN` without support for number objects.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
 */
function baseIsNaN(value) {
  return value !== value;
}

/**
 * A specialized version of `_.indexOf` which performs strict equality
 * comparisons of values, i.e. `===`.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {*} value The value to search for.
 * @param {number} fromIndex The index to search from.
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function strictIndexOf(array, value, fromIndex) {
  var index = fromIndex - 1,
      length = array.length;

  while (++index < length) {
    if (array[index] === value) {
      return index;
    }
  }
  return -1;
}

/**
 * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {*} value The value to search for.
 * @param {number} fromIndex The index to search from.
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function baseIndexOf(array, value, fromIndex) {
  return value === value
    ? strictIndexOf(array, value, fromIndex)
    : baseFindIndex(array, baseIsNaN, fromIndex);
}

/**
 * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol
 * that is not found in the character symbols.
 *
 * @private
 * @param {Array} strSymbols The string symbols to inspect.
 * @param {Array} chrSymbols The character symbols to find.
 * @returns {number} Returns the index of the last unmatched string symbol.
 */
function charsEndIndex(strSymbols, chrSymbols) {
  var index = strSymbols.length;

  while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
  return index;
}

/**
 * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol
 * that is not found in the character symbols.
 *
 * @private
 * @param {Array} strSymbols The string symbols to inspect.
 * @param {Array} chrSymbols The character symbols to find.
 * @returns {number} Returns the index of the first unmatched string symbol.
 */
function charsStartIndex(strSymbols, chrSymbols) {
  var index = -1,
      length = strSymbols.length;

  while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
  return index;
}

/**
 * Converts an ASCII `string` to an array.
 *
 * @private
 * @param {string} string The string to convert.
 * @returns {Array} Returns the converted array.
 */
function asciiToArray(string) {
  return string.split('');
}

/** Used to compose unicode character classes. */
var rsAstralRange = '\\ud800-\\udfff',
    rsComboMarksRange = '\\u0300-\\u036f',
    reComboHalfMarksRange = '\\ufe20-\\ufe2f',
    rsComboSymbolsRange = '\\u20d0-\\u20ff',
    rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange,
    rsVarRange = '\\ufe0e\\ufe0f';

/** Used to compose unicode capture groups. */
var rsZWJ = '\\u200d';

/** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */
var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange  + rsComboRange + rsVarRange + ']');

/**
 * Checks if `string` contains Unicode symbols.
 *
 * @private
 * @param {string} string The string to inspect.
 * @returns {boolean} Returns `true` if a symbol is found, else `false`.
 */
function hasUnicode(string) {
  return reHasUnicode.test(string);
}

/** Used to compose unicode character classes. */
var rsAstralRange$1 = '\\ud800-\\udfff',
    rsComboMarksRange$1 = '\\u0300-\\u036f',
    reComboHalfMarksRange$1 = '\\ufe20-\\ufe2f',
    rsComboSymbolsRange$1 = '\\u20d0-\\u20ff',
    rsComboRange$1 = rsComboMarksRange$1 + reComboHalfMarksRange$1 + rsComboSymbolsRange$1,
    rsVarRange$1 = '\\ufe0e\\ufe0f';

/** Used to compose unicode capture groups. */
var rsAstral = '[' + rsAstralRange$1 + ']',
    rsCombo = '[' + rsComboRange$1 + ']',
    rsFitz = '\\ud83c[\\udffb-\\udfff]',
    rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',
    rsNonAstral = '[^' + rsAstralRange$1 + ']',
    rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}',
    rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]',
    rsZWJ$1 = '\\u200d';

/** Used to compose unicode regexes. */
var reOptMod = rsModifier + '?',
    rsOptVar = '[' + rsVarRange$1 + ']?',
    rsOptJoin = '(?:' + rsZWJ$1 + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',
    rsSeq = rsOptVar + reOptMod + rsOptJoin,
    rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';

/** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */
var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');

/**
 * Converts a Unicode `string` to an array.
 *
 * @private
 * @param {string} string The string to convert.
 * @returns {Array} Returns the converted array.
 */
function unicodeToArray(string) {
  return string.match(reUnicode) || [];
}

/**
 * Converts `string` to an array.
 *
 * @private
 * @param {string} string The string to convert.
 * @returns {Array} Returns the converted array.
 */
function stringToArray(string) {
  return hasUnicode(string)
    ? unicodeToArray(string)
    : asciiToArray(string);
}

/**
 * Converts `value` to a string. An empty string is returned for `null`
 * and `undefined` values. The sign of `-0` is preserved.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to convert.
 * @returns {string} Returns the converted string.
 * @example
 *
 * _.toString(null);
 * // => ''
 *
 * _.toString(-0);
 * // => '-0'
 *
 * _.toString([1, 2, 3]);
 * // => '1,2,3'
 */
function toString(value) {
  return value == null ? '' : baseToString(value);
}

/** Used to match leading and trailing whitespace. */
var reTrim = /^\s+|\s+$/g;

/**
 * Removes leading and trailing whitespace or specified characters from `string`.
 *
 * @static
 * @memberOf _
 * @since 3.0.0
 * @category String
 * @param {string} [string=''] The string to trim.
 * @param {string} [chars=whitespace] The characters to trim.
 * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
 * @returns {string} Returns the trimmed string.
 * @example
 *
 * _.trim('  abc  ');
 * // => 'abc'
 *
 * _.trim('-_-abc-_-', '_-');
 * // => 'abc'
 *
 * _.map(['  foo  ', '  bar  '], _.trim);
 * // => ['foo', 'bar']
 */
function trim(string, chars, guard) {
  string = toString(string);
  if (string && (guard || chars === undefined)) {
    return string.replace(reTrim, '');
  }
  if (!string || !(chars = baseToString(chars))) {
    return string;
  }
  var strSymbols = stringToArray(string),
      chrSymbols = stringToArray(chars),
      start = charsStartIndex(strSymbols, chrSymbols),
      end = charsEndIndex(strSymbols, chrSymbols) + 1;

  return castSlice(strSymbols, start, end).join('');
}

/** `Object#toString` result references. */
var stringTag = '[object String]';

/**
 * Checks if `value` is classified as a `String` primitive or object.
 *
 * @static
 * @since 0.1.0
 * @memberOf _
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a string, else `false`.
 * @example
 *
 * _.isString('abc');
 * // => true
 *
 * _.isString(1);
 * // => false
 */
function isString(value) {
  return typeof value == 'string' ||
    (!isArray(value) && isObjectLike(value) && baseGetTag(value) == stringTag);
}

// bunch of generic helpers

/**
 * DIY in Array
 * @param {array} arr to check from
 * @param {*} value to check against
 * @return {boolean} true on found
 */
var inArray = function (arr, value) { return !!arr.filter(function (a) { return a === value; }).length; };

/**
 * @param {object} obj for search
 * @param {string} key target
 * @return {boolean} true on success
 */
var isObjectHasKey = function(obj, key) {
  var keys = Object.keys(obj);
  return inArray(keys, key)
};

/**
 * create a event name
 * @param {string[]} args
 * @return {string} event name for use
 */
var createEvt = function () {
  var args = [], len = arguments.length;
  while ( len-- ) args[ len ] = arguments[ len ];

  return args.join('_');
};

/**
 * @param {boolean} sec return in second or not
 * @return {number} timestamp
 */
var timestamp = function (sec) {
  if ( sec === void 0 ) sec = false;

  var time = Date.now();
  return sec ? Math.floor( time / 1000 ) : time;
};

/**
 * construct a url with query parameters
 * @param {string} url to append
 * @param {object} params to append to url
 * @return {string} url with appended params
 */
var urlParams = function (url, params) {
  var parts = [];
  for (var key in params) {
    parts.push( [key, params[key]].join('=') );
  }
  return [url, parts.join('&')].join('?')
};

/**
 * construct a url with cache burster
 * @param {string} url to append to
 * @return {object} _cb key timestamp
 */
var cacheBurstUrl = function (url) { return urlParams(url, cacheBurst()); };

/**
 * @return {object} _cb as key with timestamp
 */
var cacheBurst = function () { return ({ _cb: timestamp() }); };

/**
 * From underscore.string library
 * @BUG there is a bug here with the non-standard name start with _
 * @param {string} str string
 * @return {string} dasherize string
 */
var dasherize = function (str) { return (
  trim(str)
    .replace(/([A-Z])/g, '-$1')
    .replace(/[-_\s]+/g, '-')
    .toLowerCase()
); };

/**
 * simple util method to get the value
 * @param {string} name of the key
 * @param {object} obj to take value from
 * @return {*} the object value id by name or undefined
 */
var getConfigValue = function (name, obj) { return (
  obj && isPlainObject(obj) ? ( (name in obj) ? obj[name] : undefined ) : undefined
); };

/**
 * Check several parameter that there is something in the param
 * @param {*} param input
 * @return {boolean}
 */
var isNotEmpty = function(param) {
  return param !== undefined && param !== false && param !== null && trim(param) !== '';
};

// the core stuff to id if it's calling with jsonql
var DATA_KEY = 'data';
var ERROR_KEY = 'error';

// @TODO remove this is not in use
// export const CLIENT_CONFIG_FILE = '.clients.json';
// export const CONTRACT_CONFIG_FILE = 'jsonql-contract-config.js';
// type of resolvers
var QUERY_NAME = 'query';
var MUTATION_NAME = 'mutation';
var SOCKET_NAME = 'socket';
// for calling the mutation
var PAYLOAD_PARAM_NAME = 'payload';
var CONDITION_PARAM_NAME = 'condition';
var RESOLVER_PARAM_NAME = 'resolverName';
var QUERY_ARG_NAME = 'args';

// methods allow
var API_REQUEST_METHODS = ['POST', 'PUT'];
var NO_STATUS_CODE = -1;

/**
 * some time it's hard to tell where the error is throw from
 * because client server throw the same, therefore this util fn
 * to add a property to the error object to tell if it's throw
 * from client or server
 *
 */

var isBrowser = function () {
  try {
    if (window || document) {
      return true;
    }
  } catch(e) {}
  return false;
};

var isNode = function () {
  try {
    if (!isBrowser() && global$1) {
      return true;
    }
  } catch(e) {}
  return false;
};

function whereAmI() {
  if (isBrowser()) {
    return 'browser'
  }
  if (isNode()) {
    return 'node'
  }
  return 'unknown'
}

// The base Error of all

var JsonqlBaseError = /*@__PURE__*/(function (Error) {
  function JsonqlBaseError() {
    var args = [], len = arguments.length;
    while ( len-- ) args[ len ] = arguments[ len ];

    Error.apply(this, args);
  }

  if ( Error ) JsonqlBaseError.__proto__ = Error;
  JsonqlBaseError.prototype = Object.create( Error && Error.prototype );
  JsonqlBaseError.prototype.constructor = JsonqlBaseError;

  JsonqlBaseError.where = function where () {
    return whereAmI()
  };

  return JsonqlBaseError;
}(Error));

/**
 * This is a custom error to throw when could not find the resolver
 * This help us to capture the right error, due to the call happens in sequence
 * @param {string} message to tell what happen
 * @param {mixed} extra things we want to add, 500?
 */
var JsonqlResolverNotFoundError = /*@__PURE__*/(function (JsonqlBaseError) {
  function JsonqlResolverNotFoundError() {
    var args = [], len = arguments.length;
    while ( len-- ) args[ len ] = arguments[ len ];

    JsonqlBaseError.apply(this, args);

    this.message = args[0];
    this.detail = args[1];

    this.className = JsonqlResolverNotFoundError.name;

    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, JsonqlResolverNotFoundError);
    }
  }

  if ( JsonqlBaseError ) JsonqlResolverNotFoundError.__proto__ = JsonqlBaseError;
  JsonqlResolverNotFoundError.prototype = Object.create( JsonqlBaseError && JsonqlBaseError.prototype );
  JsonqlResolverNotFoundError.prototype.constructor = JsonqlResolverNotFoundError;

  var staticAccessors = { statusCode: { configurable: true },name: { configurable: true } };

  staticAccessors.statusCode.get = function () {
    return 404;
  };

  staticAccessors.name.get = function () {
    return 'JsonqlResolverNotFoundError';
  };

  Object.defineProperties( JsonqlResolverNotFoundError, staticAccessors );

  return JsonqlResolverNotFoundError;
}(JsonqlBaseError));

// custom validation error class
// when validaton failed
var JsonqlValidationError = /*@__PURE__*/(function (JsonqlBaseError) {
  function JsonqlValidationError() {
    var args = [], len = arguments.length;
    while ( len-- ) args[ len ] = arguments[ len ];

    JsonqlBaseError.apply(this, args);

    this.message = args[0];
    this.detail = args[1];

    this.className = JsonqlValidationError.name;

    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, JsonqlValidationError);
    }
  }

  if ( JsonqlBaseError ) JsonqlValidationError.__proto__ = JsonqlBaseError;
  JsonqlValidationError.prototype = Object.create( JsonqlBaseError && JsonqlBaseError.prototype );
  JsonqlValidationError.prototype.constructor = JsonqlValidationError;

  var staticAccessors = { name: { configurable: true } };

  staticAccessors.name.get = function () {
    return 'JsonqlValidationError';
  };

  Object.defineProperties( JsonqlValidationError, staticAccessors );

  return JsonqlValidationError;
}(JsonqlBaseError));

/**
 * This is a custom error to throw whenever a error happen inside the jsonql
 * This help us to capture the right error, due to the call happens in sequence
 * @param {string} message to tell what happen
 * @param {mixed} extra things we want to add, 500?
 */
var JsonqlError = /*@__PURE__*/(function (JsonqlBaseError) {
  function JsonqlError() {
    var args = [], len = arguments.length;
    while ( len-- ) args[ len ] = arguments[ len ];

    JsonqlBaseError.apply(this, args);

    this.message = args[0];
    this.detail = args[1];

    this.className = JsonqlError.name;

    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, JsonqlError);
      // this.detail = this.stack;
    }
  }

  if ( JsonqlBaseError ) JsonqlError.__proto__ = JsonqlBaseError;
  JsonqlError.prototype = Object.create( JsonqlBaseError && JsonqlBaseError.prototype );
  JsonqlError.prototype.constructor = JsonqlError;

  var staticAccessors = { name: { configurable: true },statusCode: { configurable: true } };

  staticAccessors.name.get = function () {
    return 'JsonqlError';
  };

  staticAccessors.statusCode.get = function () {
    return NO_STATUS_CODE;
  };

  Object.defineProperties( JsonqlError, staticAccessors );

  return JsonqlError;
}(JsonqlBaseError));

// split the contract into the node side and the generic side
/**
 * Check if the json is a contract file or not
 * @param {object} contract json object
 * @return {boolean} true
 */
function checkIsContract(contract) {
  return isPlainObject(contract)
  && (
    isObjectHasKey(contract, QUERY_NAME)
 || isObjectHasKey(contract, MUTATION_NAME)
 || isObjectHasKey(contract, SOCKET_NAME)
  )
}

/**
 * Ported from jsonql-params-validator but different
 * if we don't find the socket part then return false
 * @param {object} contract the contract object
 * @return {object|boolean} false on failed
 */
function extractSocketPart(contract) {
  if (isObjectHasKey(contract, 'socket')) {
    return contract.socket;
  }
  return false;
}

/**
 * @BUG we should check the socket part instead of expect the downstream to read the menu!
 * We only need this when the enableAuth is true otherwise there is only one namespace
 * @param {object} contract the socket part of the contract file
 * @param {boolean} [fallback=false] this is a fall back option for old code
 * @return {object} 1. remap the contract using the namespace --> resolvers
 * 2. the size of the object (1 all private, 2 mixed public with private)
 * 3. which namespace is public
 */
function groupByNamespace(contract, fallback) {
  if ( fallback === void 0 ) fallback = false;

  var socket = extractSocketPart(contract);
  if (socket === false) {
    if (fallback) {
      return contract; // just return the whole contract
    }
    throw new JsonqlError("socket not found in contract!")
  }
  var nspSet = {};
  var size = 0;
  var publicNamespace;
  for (var resolverName in socket) {
    var params = socket[resolverName];
    var namespace = params.namespace;
    if (namespace) {
      if (!nspSet[namespace]) {
        ++size;
        nspSet[namespace] = {};
      }
      nspSet[namespace][resolverName] = params;
      if (!publicNamespace) {
        if (params.public) {
          publicNamespace = namespace;
        }
      }
    }
  }
  return { size: size, nspSet: nspSet, publicNamespace: publicNamespace }
}

/**
 * Extract the args from the payload
 * @param {object} payload to work with
 * @param {string} type of call
 * @return {array} args
 */
function extractArgsFromPayload(payload, type) {
  switch (type) {
    case QUERY_NAME:
      return payload[QUERY_ARG_NAME];
    case MUTATION_NAME:
      return [
        payload[PAYLOAD_PARAM_NAME],
        payload[CONDITION_PARAM_NAME]
      ];
    default:
      throw new JsonqlError(("Unknown " + type + " to extract argument from!"))
  }
}

/**
 * Like what the name said
 * @param {object} contract the contract json
 * @param {string} type query|mutation
 * @param {string} name of the function
 * @return {object} the params part of the contract
 */
function extractParamsFromContract(contract, type, name) {
  try {
    var result = contract[type][name];
    // debug('extractParamsFromContract', result)
    if (!result) {
      // debug(name, type, contract)
      throw new JsonqlResolverNotFoundError(name, type)
    }
    return result;
  } catch(e) {
    throw new JsonqlResolverNotFoundError(name, e)
  }
}

// ported from jsonql-params-validator

/**
 * make sure it's an object (it was call formatPayload but it doesn't make sense)
 * @param {*} payload the object comes in could be string based
 * @return {object} the transformed payload
 */
var toPayload = function (payload) { return isString(payload) ? JSON.parse(payload) : payload; };

/**
 * @param {*} args arguments to send
 *@return {object} formatted payload
 */
var formatPayload = function (args) {
  var obj;

  return (
  ( obj = {}, obj[QUERY_ARG_NAME] = args, obj )
);
};

/**
 * Get name from the payload (ported back from jsonql-koa)
 * @param {*} payload to extract from
 * @return {string} name
 */
function getNameFromPayload(payload) {
  return Object.keys(payload)[0]
}

/**
 * @param {string} resolverName name of function
 * @param {array} [args=[]] from the ...args
 * @param {boolean} [jsonp = false] add v1.3.0 to koa
 * @return {object} formatted argument
 */
function createQuery(resolverName, args, jsonp) {
  var obj;

  if ( args === void 0 ) args = [];
  if ( jsonp === void 0 ) jsonp = false;
  if (isString(resolverName) && isArray(args)) {
    var payload = formatPayload(args);
    if (jsonp === true) {
      return payload;
    }
    return ( obj = {}, obj[resolverName] = payload, obj )
  }
  throw new JsonqlValidationError("[createQuery] expect resolverName to be string and args to be array!", { resolverName: resolverName, args: args })
}

// string version of the above
function createQueryStr(resolverName, args, jsonp) {
  if ( args === void 0 ) args = [];
  if ( jsonp === void 0 ) jsonp = false;

  return JSON.stringify(createQuery(resolverName, args, jsonp))
}

/**
 * @param {string} resolverName name of function
 * @param {*} payload to send
 * @param {object} [condition={}] for what
 * @param {boolean} [jsonp = false] add v1.3.0 to koa
 * @return {object} formatted argument
 */
function createMutation(resolverName, payload, condition, jsonp) {
  var obj;

  if ( condition === void 0 ) condition = {};
  if ( jsonp === void 0 ) jsonp = false;
  var _payload = {};
  _payload[PAYLOAD_PARAM_NAME] = payload;
  _payload[CONDITION_PARAM_NAME] = condition;
  if (jsonp === true) {
    return _payload;
  }
  if (isString(resolverName)) {
    return ( obj = {}, obj[resolverName] = _payload, obj )
  }
  throw new JsonqlValidationError("[createMutation] expect resolverName to be string!", { resolverName: resolverName, payload: payload, condition: condition })
}

// string version of above
function createMutationStr(resolverName, payload, condition, jsonp) {
  if ( condition === void 0 ) condition = {};
  if ( jsonp === void 0 ) jsonp = false;

  return JSON.stringify(createMutation(resolverName, payload, condition, jsonp))
}

/**
 * Further break down from method below for use else where
 * @param {string} resolverName name of fn
 * @param {object} payload payload
 * @return {object|boolean} false on failed
 */
function getQueryFromArgs(resolverName, payload) {
  var obj;

  if (resolverName && isPlainObject(payload)) {
    var args = payload[resolverName];
    if (args[QUERY_ARG_NAME]) {
      return ( obj = {}, obj[RESOLVER_PARAM_NAME] = resolverName, obj[QUERY_ARG_NAME] = args[QUERY_ARG_NAME], obj )
    }
  }
  return false;
}

/**
 * Share function so no repeat
 * @param {object} payload the payload from client
 * @param {function} processor the last get result method
 * @return {*} result processed result
 */
function processPayload(payload, processor) {
  var p = toPayload(payload);
  var resolverName = getNameFromPayload(p);
  return Reflect.apply(processor, null, [resolverName, p])
}

/**
 * extra the payload back
 * @param {*} payload from http call
 * @return {object} resolverName and args
 */
function getQueryFromPayload(payload) {
  var result = processPayload(payload, getQueryFromArgs);
  if (result !== false) {
    return result;
  }
  throw new JsonqlValidationError('[getQueryArgs] Payload is malformed!', payload)
}

/**
 * Further break down from method below for use else where
 * @param {string} resolverName name of fn
 * @param {object} payload payload
 * @return {object|boolean} false on failed
 */
function getMutationFromArgs(resolverName, payload) {
  var obj;

  if (resolverName && isPlainObject(payload)) {
    var args = payload[resolverName];
    if (args) {
      return ( obj = {}, obj[RESOLVER_PARAM_NAME] = resolverName, obj[PAYLOAD_PARAM_NAME] = args[PAYLOAD_PARAM_NAME], obj[CONDITION_PARAM_NAME] = args[CONDITION_PARAM_NAME], obj )
    }
  }
  return false;
}

/**
 * @param {object} payload
 * @return {object} resolverName, payload, conditon
 */
function getMutationFromPayload(payload) {
  var result = processPayload(payload, getMutationFromArgs);

  if (result !== false) {
    return result;
  }
  throw new JsonqlValidationError('[getMutationArgs] Payload is malformed!', payload)
}

// break up from node-middleware
/**
 * getting what is calling after the above check
 * @param {string} method of call
 * @return {mixed} false on failed
 */
var getCallMethod = function (method) {
  var POST = API_REQUEST_METHODS[0];
  var PUT = API_REQUEST_METHODS[1];
  switch (true) {
    case method === POST:
      return QUERY_NAME;
    case method === PUT:
      return MUTATION_NAME;
    default:
      return false;
  }
};

/**
 * wrapper method
 * @param {mixed} result of fn return
 * @return {string} stringify data
 */
var packResult = function(result) {
  var obj;

  return JSON.stringify(( obj = {}, obj[DATA_KEY] = result, obj ))
};

/**
 * wrapper method - the output is trying to match up the structure of the Error sub class
 * @param {mixed} detail of fn error
 * @param {string} [className=JsonqlError] the errorName
 * @param {number} [statusCode=500] the original error code
 * @return {string} stringify error
 */
var packError = function(detail, className, statusCode, message) {
  var obj;

  if ( className === void 0 ) className = 'JsonqlError';
  if ( statusCode === void 0 ) statusCode = 500;
  if ( message === void 0 ) message = '';
  return JSON.stringify(( obj = {}, obj[ERROR_KEY] = { detail: detail, className: className, statusCode: statusCode, message: message }, obj ))
};

// ported from http-client

/**
 * handle the return data
 * @param {object} result return from server
 * @return {object} strip the data part out, or if the error is presented
 */
var resultHandler = function (result) { return (
  (isObjectHasKey(result, DATA_KEY) && !isObjectHasKey(result, ERROR_KEY)) ? result[DATA_KEY] : result
); };

// exportfor ES modules

// alias
var isContract = checkIsContract;
var VERSION = '0.6.9';

export { VERSION, cacheBurst, cacheBurstUrl, chainFns, chainPromises, checkIsContract, createEvt, createMutation, createMutationStr, createQuery, createQueryStr, dasherize, extractArgsFromPayload, extractParamsFromContract, extractSocketPart, formatPayload, getCallMethod, getConfigValue, getMutationFromArgs, getMutationFromPayload, getNameFromPayload, getQueryFromArgs, getQueryFromPayload, groupByNamespace, inArray, injectToFn, isContract, isObjectHasKey, isNotEmpty, objDefineProps, packError, packResult, resultHandler, timestamp, toPayload, urlParams };
